import { Component, Input, AfterViewInit, ViewChild, ElementRef, OnInit } from '@angular/core';
import * as Diff2Html from 'diff2html';
import hljs from 'highlight.js';
import 'diff2html/bundles/css/diff2html.min.css';

@Component({
  selector: 'app-diff-viewer',
  standalone: true,
  templateUrl: './diff-viewer.component.html',
  styleUrl: './diff-viewer.component.css'
})
export class DiffViewerComponent implements OnInit, AfterViewInit {
  @Input() diffString: string = '';
  @ViewChild('diffContainer', { static: false }) diffContainer!: ElementRef;
  
  // 性能优化配置
  private isLargeDiff = false;
  private chunkSize = 500; // 每批处理的差异点数
  private isRendering = false;
  
  // 虚拟滚动相关
  private visibleLines = 50; // 初始可见行数
  private lineHeight = 24; // 每行高度估计

  ngOnInit() {
    // 检测是否为大型diff
    this.isLargeDiff = this.diffString.length > 10000; // 可根据实际情况调整阈值
  }

  ngAfterViewInit() {
    if (this.diffString && this.diffContainer) {
      // 添加滚动事件监听以实现虚拟滚动
      this.diffContainer.nativeElement.addEventListener('scroll', this.handleScroll.bind(this));
      
      // 分块渲染diff
      this.renderDiffInChunks();
    }
  }

  private renderDiffInChunks() {
    // 对于大型diff，使用更高效的配置
    const diffConfig = this.isLargeDiff ? {
      drawFileList: false, // 关闭文件列表以减少渲染负担
      matching: 'none' as const, // 关闭匹配以提高性能
      outputFormat: 'side-by-side' as any, // 行内显示比并排更节省内存
      showLineNumbers: false, // 可选：关闭行号
      highlight: false // 关闭内置高亮，使用我们自己的分块高亮
    } : {
      drawFileList: true,
      matching: 'lines' as const,
      outputFormat: 'side-by-side' as any
    };

    try {
      // 直接计算diff字符串中的差异点数量（简单方法）
      const simpleChangeCount = this.countChangesInDiffString();
      
      // 解析diff
      let diffJson;
      let parsedChangeCount = 0;
      let parsingError = '';
      
      try {
        diffJson = Diff2Html.parse(this.diffString);
        parsedChangeCount = this.countTotalChanges(diffJson);
      } catch (parseError) {
        parsingError = parseError instanceof Error ? parseError.message : '解析失败';
        console.error('解析diff时出错:', parseError);
      }
      
      // 在页面上显示详细的差异点统计和调试信息
      const statsElement = document.createElement('div');
      statsElement.className = 'diff-stats';
      statsElement.innerHTML = `
        <div class="stats-info">
          <h3>差异点统计</h3>
          <p>简单计算差异点: <strong>${simpleChangeCount}</strong></p>
          <p>解析后差异点: <strong>${parsedChangeCount}</strong></p>
          ${parsingError ? `<p class="error-text">解析错误: ${parsingError}</p>` : ''}
          <p>Diff字符串长度: ${this.diffString.length} 字符</p>
          <p>是否超过阈值: ${simpleChangeCount > 3000 ? '是' : '否'}</p>
        </div>
        <div class="debug-info">
          <h4>Debug Info:</h4>
          <p>isLargeDiff: ${this.isLargeDiff}</p>
          <p>diffString starts with: ${this.diffString.substring(0, 50)}...</p>
        </div>
      `;
      
      this.diffContainer.nativeElement.innerHTML = '';
      this.diffContainer.nativeElement.appendChild(statsElement);
      
      // 当差异点超过3000个时，合并为单一差异点
      // 使用简单计算的差异点数作为主要判断依据
      if (simpleChangeCount > 3000 && diffJson) {
        this.renderMergedDiff(diffJson, simpleChangeCount);
      } 
      // 对于单行大量差异的情况，进行特殊处理
      else if (diffJson && this.isSingleLineLargeDiff(diffJson)) {
        this.renderSingleLineDiff(diffJson, diffConfig);
      } else if (diffJson) {
        // 常规渲染
        // 生成HTML后移除ins和del节点的渲染
        let html = Diff2Html.html(diffJson, diffConfig);
        
        // 使用DOMParser来处理HTML字符串
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        
        // 移除所有ins和del节点，保留其内容
        const insNodes = doc.querySelectorAll('ins');
        insNodes.forEach(node => {
          const parent = node.parentNode;
          if (parent) {
            while (node.firstChild) {
              parent.insertBefore(node.firstChild, node);
            }
            parent.removeChild(node);
          }
        });
        
        const delNodes = doc.querySelectorAll('del');
        delNodes.forEach(node => {
          const parent = node.parentNode;
          if (parent) {
            while (node.firstChild) {
              parent.insertBefore(node.firstChild, node);
            }
            parent.removeChild(node);
          }
        });
        
        // 获取处理后的HTML
        html = doc.body.innerHTML;
        // 保留统计信息，将diff内容添加在其下方
        const diffContent = document.createElement('div');
        diffContent.innerHTML = html;
        this.diffContainer.nativeElement.appendChild(diffContent);
        
        // 分块应用代码高亮
        this.highlightCodeBlocksInChunks();
      } else {
        // 如果解析失败，显示原始diff的一部分
        const rawDiffPreview = document.createElement('div');
        rawDiffPreview.className = 'raw-diff-preview';
        rawDiffPreview.innerHTML = `
          <h4>原始Diff预览:</h4>
          <pre>${this.escapeHtml(this.diffString.substring(0, 1000))}${this.diffString.length > 1000 ? '... (truncated)' : ''}</pre>
        `;
        this.diffContainer.nativeElement.appendChild(rawDiffPreview);
      }
    } catch (error) {
      console.error('渲染diff时出错:', error);
      this.diffContainer.nativeElement.innerHTML = `<div class="error">无法渲染diff: ${error instanceof Error ? error.message : '未知错误'}</div>`;
    }
  }
  
  private countChangesInDiffString(): number {
    // 简单直接的方法：计算diff字符串中的+和-行数量
    const lines = this.diffString.split('\n');
    let changeCount = 0;
    
    lines.forEach(line => {
      if (line.startsWith('+') && !line.startsWith('+++')) {
        changeCount++;
      } else if (line.startsWith('-') && !line.startsWith('---')) {
        changeCount++;
      }
    });
    
    console.log('简单计算差异点数量:', changeCount);
    return changeCount;
  }
  
  private countTotalChanges(diffJson: any): number {
    // 计算diff中的总差异点数
    let totalChanges = 0;
    let fileCount = 0;
    let hunkCount = 0;
    let lineCount = 0;
    
    console.log('开始计算差异点:', diffJson);
    
    if (!diffJson || !Array.isArray(diffJson)) {
      console.log('diffJson不是有效的数组');
      return 0;
    }
    
    fileCount = diffJson.length;
    console.log(`找到 ${fileCount} 个文件`);
    
    diffJson.forEach((file, fileIndex) => {
      console.log(`处理文件 ${fileIndex + 1}:`, file);
      
      if (!file.hunks || !Array.isArray(file.hunks)) {
        console.log(`文件 ${fileIndex + 1} 没有有效的hunks`);
        return;
      }
      
      hunkCount += file.hunks.length;
      console.log(`文件 ${fileIndex + 1} 包含 ${file.hunks.length} 个hunks`);
      
      file.hunks.forEach((hunk: any, hunkIndex: number) => {
        if (!hunk.lines || !Array.isArray(hunk.lines)) {
          console.log(`Hunk ${hunkIndex + 1} 没有有效的lines`);
          return;
        }
        
        lineCount += hunk.lines.length;
        
        // 计算所有非普通行的数量作为差异点数
        const changesInHunk = hunk.lines.filter((line: any) => line.type !== 'normal').length;
        const addedLines = hunk.lines.filter((line: any) => line.type === 'add').length;
        const deletedLines = hunk.lines.filter((line: any) => line.type === 'delete').length;
        
        console.log(`Hunk ${hunkIndex + 1} 包含 ${hunk.lines.length} 行，${changesInHunk} 个差异点（+${addedLines}, -${deletedLines}）`);
        
        totalChanges += changesInHunk;
      });
    });
    
    console.log(`总计: ${fileCount} 个文件, ${hunkCount} 个hunks, ${lineCount} 行, ${totalChanges} 个差异点`);
    
    return totalChanges;
  }
  
  private renderMergedDiff(diffJson: any, changesCount: number) {
    // 渲染合并后的差异视图，将超过5000个的差异点合并为一个
    const fileData = diffJson[0]; // 假设只有一个文件
    const oldFileName = fileData.oldName || 'original_file';
    const newFileName = fileData.newName || 'modified_file';
    
    // 创建简化的合并差异视图
    const mergedHtml = `
      <div class="d2h-file-wrapper">
        <div class="d2h-file-header">
          <div class="d2h-file-name">${oldFileName} → ${newFileName}</div>
          <div class="d2h-file-stats">
            <span class="d2h-lines-added">+ ${changesCount}</span>
            <span class="d2h-lines-deleted">- ${changesCount}</span>
          </div>
        </div>
        <div class="d2h-file-diff">
          <div class="d2h-diff-container">
            <div class="merged-diff-summary">
              <div class="merged-diff-info">
                <p>检测到 <strong>${changesCount}</strong> 个差异点，已合并显示以优化性能。</p>
                <p>文件类型: XML</p>
                <p>差异概览: 大量内容已修改、添加和删除</p>
              </div>
              <div class="merged-diff-actions">
                <button id="viewFullDiffBtn" class="view-full-diff-btn">
                  查看完整差异 (可能影响性能)
                </button>
              </div>
              <div class="merged-diff-preview">
                <div class="preview-header">简化预览:</div>
                <div class="preview-content">
                  <pre class="simplified-diff">--- ${oldFileName}
+++ ${newFileName}
@@ -1 +1 @@
-[大量内容已省略，共 ${changesCount} 个差异点]
+[大量内容已省略，共 ${changesCount} 个差异点]</pre>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
    
    this.diffContainer.nativeElement.innerHTML = mergedHtml;
    
    // 添加查看完整差异按钮的事件监听
    const viewFullDiffBtn = this.diffContainer.nativeElement.querySelector('#viewFullDiffBtn');
    if (viewFullDiffBtn) {
      viewFullDiffBtn.addEventListener('click', () => this.renderFullDiff(diffJson));
    }
  }
  
  private renderFullDiff(diffJson: any) {
    // 渲染完整的差异（不进行合并）
    const diffConfig = {
      drawFileList: true,
      matching: 'none' as const, // 即使查看完整差异也使用更高效的匹配方式
      outputFormat: 'line-by-line' as any, // 行内显示更节省内存
      highlight: false
    };
    
    try {
      const html = Diff2Html.html(diffJson, diffConfig);
      this.diffContainer.nativeElement.innerHTML = html;
      this.highlightCodeBlocksInChunks();
    } catch (error) {
      console.error('渲染完整diff时出错:', error);
      this.diffContainer.nativeElement.innerHTML = `<div class="error">无法渲染完整diff: ${error instanceof Error ? error.message : '未知错误'}</div>`;
    }
  }

  private isSingleLineLargeDiff(diffJson: any): boolean {
    // 检查是否为单行且包含大量差异
    if (diffJson && diffJson.length === 1) {
      const hunks = diffJson[0].hunks;
      if (hunks && hunks.length === 1) {
        const lines = hunks[0].lines;
        // 计算差异点数
        const changesCount = lines.filter((line: any) => line.type !== 'normal').length;
        return changesCount > 1000; // 超过1000个差异点视为大量
      }
    }
    return false;
  }

  private renderSingleLineDiff(diffJson: any, config: any) {
    // 对单行大量差异进行特殊优化
    const fileData = diffJson[0];
    const hunk = fileData.hunks[0];
    
    // 创建简化的HTML结构，避免生成过多DOM节点
    let simplifiedHtml = `
      <div class="d2h-file-wrapper">
        <div class="d2h-file-header">
          <div class="d2h-file-name">${fileData.newName || fileData.oldName}</div>
        </div>
        <div class="d2h-file-diff">
          <div class="d2h-diff-container">
            <div class="d2h-file-side-diff">
              <div class="d2h-code-wrapper">
                <div class="d2h-code">
    `;
    
    // 只显示部分差异，添加加载更多按钮
    const visibleChanges = hunk.lines.slice(0, this.visibleLines);
    
    visibleChanges.forEach((line: any) => {
      const lineClass = `d2h-${line.type}`;
      simplifiedHtml += `
        <div class="${lineClass}">
          <div class="d2h-code-line">${this.escapeHtml(line.content)}</div>
        </div>
      `;
    });
    
    if (hunk.lines.length > this.visibleLines) {
      simplifiedHtml += `
          <div class="d2h-code-line" style="text-align: center; color: #666;">
            <button id="loadMoreBtn" style="padding: 8px 16px; background: #f0f0f0; border: none; border-radius: 4px; cursor: pointer;">
              加载更多差异 (${hunk.lines.length - this.visibleLines} 行)
            </button>
          </div>
      `;
    }
    
    simplifiedHtml += `
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
    
    this.diffContainer.nativeElement.innerHTML = simplifiedHtml;
    
    // 添加加载更多按钮事件
    const loadMoreBtn = this.diffContainer.nativeElement.querySelector('#loadMoreBtn');
    if (loadMoreBtn) {
      loadMoreBtn.addEventListener('click', () => this.loadMoreDiff(diffJson, config));
    }
  }

  private loadMoreDiff(diffJson: any, config: any) {
    // 加载更多差异内容
    this.visibleLines += 1000; // 每次多加载1000行
    this.renderSingleLineDiff(diffJson, config);
  }

  private highlightCodeBlocksInChunks() {
    // 使用requestAnimationFrame和setTimeout分块处理，避免阻塞主线程
    const codeBlocks = Array.from(this.diffContainer.nativeElement.querySelectorAll('pre code'));
    let currentIndex = 0;
    
    const processNextChunk = () => {
      if (currentIndex >= codeBlocks.length) {
        this.isRendering = false;
        return;
      }
      
      // 处理当前批次
      const endIndex = Math.min(currentIndex + this.chunkSize, codeBlocks.length);
      const currentChunk = codeBlocks.slice(currentIndex, endIndex);
      
      currentChunk.forEach((block: any) => {
        try {
          hljs.highlightElement(block);
        } catch (error) {
          console.error('高亮代码块时出错:', error);
        }
      });
      
      currentIndex = endIndex;
      
      // 使用setTimeout避免长时间阻塞主线程
      setTimeout(() => {
        requestAnimationFrame(processNextChunk);
      }, 50);
    };
    
    if (!this.isRendering) {
      this.isRendering = true;
      processNextChunk();
    }
  }

  private handleScroll() {
    // 虚拟滚动处理逻辑
    const container = this.diffContainer.nativeElement;
    const scrollTop = container.scrollTop;
    const scrollHeight = container.scrollHeight;
    const clientHeight = container.clientHeight;
    
    // 当滚动到底部时，可以触发加载更多内容
    if (scrollTop + clientHeight >= scrollHeight - 100) {
      // 可以在这里实现加载更多逻辑
    }
  }

  private escapeHtml(text: string): string {
    // 简单的HTML转义，防止XSS
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }
}
